﻿package org.hive.managers.plugin
{
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.system.System;
	import org.hive.Hive;
	import org.hive.sequence.Sequence;
	import org.hive.utils.performance.MemoryTracker;
	
	import caurina.transitions.TweenListObj;
	import org.hive.managers.*;
	import org.hive.managers.utils.LayoutAlign;
	import org.hive.managers.utils.LayoutArrange;
	import org.hive.layout.*;
	import org.hive.events.*;
	import org.hive.animation.*;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.text.TextFieldAutoSize;
	
	import fl.motion.*;
	
	import flash.events.IEventDispatcher;
	import flash.display.*;
	import flash.events.TimerEvent;
	import flash.events.Event;
	
	import caurina.transitions.*;
		
	public class LayoutManager extends Manager
	{
        /**
        *
        */
		private static var __stage : DisplayObjectContainer;
		
        /**
        *
        */
		private static var __stageWidth : Number = 0;

        /**
        *
        */
		private static var __stageHeight : Number = 0;
		
		/**
        *
        */
		private static var __memoryTracker : MemoryTracker;
		

		public static var initialized:Boolean = false;
		
		
		public static function init():void
		{
			if( !initialized ) {
				trace( "LayoutManager :: initializing" );
				
				//_____________ Register Object :: Animator
				function animatorFunc( action:*, sequence:Sequence, positionInSequence:int ):void {
					var animation:Animator = action as Animator;
					if( sequence.index( positionInSequence - 1 ) as MotionDelay  ){
						if( sequence.getParams( action ) != null )
						LayoutManager.addChildAt( animation.target, sequence.getParams( action )[0] );
						else LayoutManager.addChild( animation.target );
					}
					else {
						LayoutManager.execute( action );
					}
				}
				StateManager.registerAction(Animator, animatorFunc);
				
				//_____________ Register Object :: MotionDelay
				function motionDelayFunc( action:*, sequence:Sequence, positionInSequence:int ):void {
					var animatorAction:Animator;
					if( sequence.length > ( positionInSequence + 1 ) &&
					  ( animatorAction = sequence.index( positionInSequence + 1 ) as Animator ) != null ){
						action.start( onMotionDelayComplete, animatorAction );
					}
				}
				StateManager.registerAction(MotionDelay, motionDelayFunc);
				
				//_____________ Register Object :: DisplayObject
				function displayObjectFunc( action:*, sequence:Sequence, positionInSequence:int ):void {
					if( sequence.getParams( action ) != null){
						LayoutManager.addChildAt( action, sequence.getParams( action )[0] );
					} else LayoutManager.addChild( action );
				}
				StateManager.registerAction(DisplayObject, displayObjectFunc);
				
				//_____________ Register Object :: DisplayObject : Tweener
				function tweenableDisplayObjectFunc( action:*, sequence:Sequence, positionInSequence:int ):void {
					var scope:DisplayObject = action.scope as DisplayObject;
					delete action.scope;
					LayoutManager.tween( scope, action );
				}
				StateManager.registerAction(DisplayObject, tweenableDisplayObjectFunc, true);

				__stage = StateManager.documentClass;
				
				__stageWidth = __stage.width;
				__stageHeight = __stage.height;
				
				if( Hive.APPLICATION_MODE == 1 ){
					__memoryTracker = new MemoryTracker( );
					__stage.stage.addEventListener( KeyboardEvent.KEY_UP, showMemoryMonitor );
				}
				
				__animationMonitor.addEventListener( Event.ENTER_FRAME, onEnterFrame );
				
				initialized = true;
			}
		}
		
		private static function showMemoryMonitor( e:KeyboardEvent )
		{
			if( String.fromCharCode(e.charCode) == 'm' && e.ctrlKey && e.altKey &&
				__stage.stage.contains( __memoryTracker ) )
				__stage.stage.removeChild( __memoryTracker );
			else if( String.fromCharCode(e.charCode) == 'm' && e.ctrlKey &&
				!__stage.stage.contains( __memoryTracker ) )
				__stage.stage.addChild( __memoryTracker );
		}
		
		public static function onMotionDelayComplete( e:MotionDelayEvent ):void
        {
            LayoutManager.execute( e.animation );
        }

		
		
        
        /**
        *
        * @param	animation
        */
		public static function execute( animation:Animator )
		{
			if( !__stage.contains( animation.target ) )
			addChild( animation.target );

			__animationArray.push( animation );
		}
		
        /**
        *
        */
		private static var __animationMonitor:MovieClip = new MovieClip( );

        /**
        *
        */
		private static var __animationArray:Array = new Array( );

        /**
        *
        * @param	e
        * @return
        */
		private static function onEnterFrame( e:Event ):void
		{
			if( __animationArray.length > 0 ) __animationArray.forEach( updateAnimation );
		}
		
        /**
        *
        * @param	item
        * @param	index
        * @param	array
        * @return
        */
		private static function updateAnimation(item:*, index:int, array:Array):void
		{
			if( item ){
				item.nextFrame();
				if( item.time == item.motion.duration-1 ){
					item.dispatchEvent( new MotionEvent( MotionEvent.MOTION_END ) );
					__animationArray.splice( __animationArray.indexOf(item), 1 );
					item = null;
				}
                else if( item.time == 1 ){
                    item.dispatchEvent( new MotionEvent( MotionEvent.MOTION_START ) );
                }
			}
		}

        /**
        *
        * @param	child
        * @param	tweenObject
        * @return
        */
		internal static function tween( child:DisplayObject, tweenObject:Object ):void
		{
			if( !contains( child ) ) addChild( child );
			Tweener.addTween( child as DisplayObject, tweenObject );
		}
		
       
		//__________________________________________________________________________________ DISPLAY LIST METHODS

        /**
        *
        * @param	element
        * @param	index
        * @return
        */
		private static function buildElement( element:*, index:Number=0 ):DisplayObject
		{
			if( index ){
				return __stage.addChildAt( element, index );
			}else{
				return __stage.addChild( element );
			}
		}
		
        /**
        *
        * @param	arrangement
        * @param	target
        * @param	relativeTo
        */
		public static function arrange( arrangement:String, target:DisplayObject, relativeTo:DisplayObject=null ):*
		{
			if( !__stage.contains( target ) ) return null;
			
			var index:Number = -1;
			switch( arrangement ){
				
				case LayoutArrange.BRING_TO_BACK:
					index = 0
				break;
				
				case LayoutArrange.BRING_TO_FRONT:
					index = __stage.numChildren - 1;
				break;
				
				case LayoutArrange.BRING_BACKWARD:
					index = __stage.getChildIndex( target ) - 1;
				break;
				
				case LayoutArrange.BRING_FORWARD:
					index = __stage.getChildIndex( target ) + 1;
				break;
				
				case LayoutArrange.BEHIND:
					index = __stage.getChildIndex( relativeTo ) - 1;
				break;
				
				case LayoutArrange.INFRONT:
					index = __stage.getChildIndex( relativeTo ) + 1;
				break;
			}
			
			if( index != -1 )	__stage.setChildIndex( target, index );
			
			return index;
		}

        /**
        *
        * @param	child
        * @return
        */
		public static function getChildIndex( child:DisplayObject ):Number
		{
			return Number(__stage.getChildIndex( child ));
		}

        /**
        *
        * @param	child
        * @return
        */
		public static function addChild( child:DisplayObject ):DisplayObject
		{
			return buildElement( child );
		}
		
        /**
        *
        * @param	child
        * @param	index
        * @return
        */
		public static function addChildAt( child:DisplayObject, index:Number=0 ):DisplayObject
		{
			return buildElement( child, index );
		}
		
        /**
        *
        * @param	...children
        * @return
        */
		public static function removeChildren( ...children ):void
		{
			for each( var child:DisplayObject in children ) removeChild( child );
		}
		
        /**
        *
        * @param	child
        * @return
        */
		public static function removeChild( child:DisplayObject ):DisplayObject
		{
			return __stage.removeChild( child );
		}
		
        /**
        *
        * @param	index
        * @return
        */
		public static function removeChildAt( index:Number ):DisplayObject
		{
			return __stage.removeChildAt( index );
		}
		
        /**
        *
        * @param	name
        * @return
        */
		public static function getChildByName( name:String ):DisplayObject
		{
			return __stage.getChildByName( name );
		}
		
        /**
        *
        * @param	index
        * @return
        */
		public static function getChildAt( index:Number ):DisplayObject
		{
			return __stage.getChildAt( index );
		}
		
        /**
        *
        * @return
        */
		public static function get stageWidth( ):Number
		{
			return __stage.stage.stageWidth;
		}
		
        /**
        *
        * @return
        */
		public static function get stageHeight( ):Number
		{
			return __stage.stage.stageHeight;
		}
		
        /**
        *
        * @return
        */
		public static function get stageCenterX( ):Number
		{
			return __stage.stage.stageWidth/2;
		}
		
        /**
        *
        * @return
        */
		public static function get stageCenterY( ):Number
		{
			return __stage.stage.stageHeight/2;
		}
		
        /**
        *
        * @return
        */
		public static function get stage( ):Stage
		{
			return __stage.stage;
		}
		
        /**
        *
        * @return
        */
		public static function get documentClass( ):DisplayObjectContainer
		{
			return __stage;
		}
		
        /**
        *
        * @param	child
        * @return
        */
		public static function contains( child:DisplayObject ):Boolean
		{
			return __stage.contains(child);
		}

        /**
        *
        * @return
        */
        public static function get stageLeft( ):Number
        {
            return __stageWidth - stageWidth / 2;
        }

        /**
        *
        * @return
        */
        public static function get stageRight( ):Number
        {
            return stageLeft + stageWidth;
        }

        /**
        *
        * @return
        */
        public static function get stageTop( ):Number
        {
            return __stageHeight - stageHeight / 2;
        }

        /**
        *
        * @return
        */
        public static function get stageBottom( ):Number
        {
            return stageTop + stageHeight;
        }
		
		
		
		
		
		//_________________________________________________________________________________________________ STATIC EVENT DISPATCHER METHODS
		protected static var disp:EventDispatcher;
		
		public static function addEventListener(p_type:String, p_listener:Function, p_useCapture:Boolean = false, p_priority:int = 0, p_useWeakReference:Boolean = false):void {
			if (disp == null) { disp = new EventDispatcher(); }
			disp.addEventListener(p_type, p_listener, p_useCapture, p_priority, p_useWeakReference);
		}
		
		public static function removeEventListener(p_type:String, p_listener:Function, p_useCapture:Boolean=false):void {
			if (disp == null) { return; }
			disp.removeEventListener(p_type, p_listener, p_useCapture);
		}
		
		public static function dispatchEvent(p_event:Event):void {
			if (disp == null) { return; }
			disp.dispatchEvent(p_event);
		}
		
	}
}
